#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "TDAClienteCorreo.h"
#include "TDACorreo.h"
#include "TDACanalSeguro.h"
#include "TDAServidorInseguro.h"

#include "logdebug.h"

/* Maxima longitud de nombre de archivo permitida */
#define MAX_FILENAME_SIZE 32

/* Extension de los archivos de entrada */
#define DEFAULT_EXTENSION ".txt"

/* Sufijo a agregar para generar los archivos de salida */
#define DEFAULT_SUFFIX "_recv.txt"

/* Longitud del sufijo a agregar (incluye el \0) */
#define SUFFIX_LENGTH 10

/*
 * Enunciado:
 *
 * Se desea simular un escenario de envio de correos desde un cliente de correo a otro,
 * a traves de un servidor. El servidor utiliza un canal inseguro para comunicarse con
 * los clientes, por lo que se ha decidido utilizar un mecanismo de encriptacion simetrica
 * para proteger el cuerpo de los correos. La clave para la encriptacion se transfiere del
 * cliente de correo emisor al receptor en forma directa por medio de un canal seguro, antes
 * del envio del correo.
 *
 * Como sistema criptografico se sugiere utilizar el cifrado de Vignere, por ser una variante
 * mas elaborada que el Caesar, pero no tan compleja como otros algoritmos existentes.
 *
 * El sistema involucra la lectura y escritura de archivos con los contenidos de los mensajes.
 * A tal fin se utilizara un formato del estilo del estipulado en la RFC 822, que se ejemplifica
 * en los archivos .txt de ejemplo provistos.
 *
 * A partir de los archivos .h provistos, y considerando que la implementacion de TDACanalSeguro
 * es provista en una biblioteca, codificar todos los .c necesarios para implementar los TDA del
 * sistema. Codificar ademas un programa de aplicacion que realice la simulacion, de acuerdo con
 * los lineamientos especificados en la funcion main. Este programa no debe tener interaccion con el
 * usuario: simplemente informara en que paso de la simulacion se encuentra y dejara todas las salidas
 * en los archivos indicados a fines de que quien ejecute el programa de aplicacion pueda leerlos.
 *
 * Aquella informacion relevante que sea necesaria para poder hacer un correcto seguimiento
 * de la simulacion (por ejemplo, la clave utilizada y el contenido del mensaje encriptado)
 * y que no sea provista por la interfaz publica de los TDA, debera indicarse por la salida de
 * error, junto con toda otra informacion de utilidad para el seguimiento de la ejecucion.
 * EXCEPCIONALMENTE, Y SOLO A LOS FINES DE NO AUMENTAR LA COMPLEJDAD EL TRABAJO, SE
 * PERMITIRA INCLUIR LLAMADAS A FUNCIONES DE ENTRADA/SALIDA EN LOS TDA PARA PODER GRABAR
 * LA INFORMACION NECESARIA PARA PERMITIR LA CORRECCION DEL TRABAJO. SIN EMBARGO, ESTAS
 * LLAMADAS DEBERAN REALIZARSE MEDIANTE LA MACRO PROVISTA EN EL ARCHIVO logdebug.h Y NO
 * LLAMANDO A fprint O FUNCIONES SIMILARES EN FORMA DIRECTA. Debera hacerse un include de
 * logdebug.h en cada archivo desde el cual se desee agregar contenido al log. Ver los
 * ejemplos de llamadas en la funcion main.
 */

int main(int argc, char **argv) {

	/* El programa de aplicacion debe leer todos los argumentos indicados por linea de comando.
	 * Cada argumento (excepto el primero, que es el nombre del propio programa) indica un
	 * archivo con un correo a importar y enviar. El programa debe enviar los correos y producir,
	 * por cada uno, un archivo con el mismo nombre que el utilizado para importar pero agregando
	 * el sufijo _recv al nombre.
	 * Si no se indica archivo alguno por linea de comando, el programa debe enviar un correo
	 * predefinido (utilizando los setters de TDACorreo) y mostrar el correo recibido por
	 * salida estandar (utilizando los getters de TDACorreo)
	 */

	/* El presente ejemplo realiza todos los crear al principio y todos los destruir al final
	 * con el proposito de aumentar la claridad para el alumno.
	 * No obstante, debe considerarse que en el programa de aplicacion definitivo las invocaciones
	 * a los destruir podrian realizarse en forma mas temprana, por lo que las primitivas de los
	 * TDA deberian realizar "copias defensivas" (esto es, no guardar en las estructuras los punteros
	 * pasados por parametro, sino utilizar los getters y setters de dichos parametros para realizar
	 * copias propias que no puedan ser destruidas desde afuera) a fines de evitar efectos secundarios.
	 */

	/* Declaro las variables que voy a utilizar */
	/* Los clientes de correo */
	TDAClienteCorreo emisor;
	TDAClienteCorreo receptor;

	/* Dos variables para los correos */
	TDACorreo correoEnviado;
	TDACorreo correoRecibido;

	/* El canal seguro */
	TDACanalSeguro canalSeguro;

	/* El servidor */
	TDAServidorInseguro servidor;

	/* Los archivos a importar y exportar */
	FILE* archivoIn;
	FILE* archivoOut;

    /* Variables empleadas solo en el caso de envio de correo estandar */
    char remitente[MAX_ADDR_SIZE];
    char destinatario[MAX_ADDR_SIZE];
    char asunto[MAX_SUBJECT_SIZE];
    char cuerpo[MAX_BODY_SIZE];
    time_t fechaHora;


    /* Variable auxiliar para generar el nombre del archivo final
     * sin la necesidad de modificar argv
     */
    char outFilename[MAX_FILENAME_SIZE];

	size_t i;

    /* Recorro todos los argumentos, a partir de el segundo elemento
     * se encuentran los argumentos que hacen referencia al nombre del
     * del archivo a abrir.
     *
     * NOTA: Por un tema de optimizacion aprovechando las caracterisiticas
     * alejdas de la realidad de este TP (trabajar con un solo destinatario,
     * un solo correo por servidor, un unico servidor, etc) se podria haber
     * haber creado el emisor, receptor, servidor y canalSeguro fuera del for
     * no obstante hemos decidido dejar esta implementacion por dos motivos:
     * 1) La generacion de la clave se hace cada vez al crear el emisor.
     * 2) Se asemeja mas a la realidad y puede ser util en futuras ampliaciones.
     */

    for ( i=0 ; i < argc ; i++){

        /* Creo los clientes, el canal seguro y el servidor */
        crearClienteCorreo(&emisor,DEFAULT_RMT);
        crearClienteCorreo(&receptor, DEFAULT_DST);
        crearCanalSeguro(&canalSeguro);
        crearServidor(&servidor);

        logmacro("\nLa clave generada por el emisor es: %s\n",emisor.clave);

        /* Envio la clave para encriptacion por el canal seguro */
        enviarClave(emisor, &canalSeguro);

        if ( conectar(&emisor, &servidor) ){
            logmacro("\n%s\n","No fue posible conectar el emisor al servidor");
            return EXIT_FAILURE;
        }

        if ( conectar(&receptor, &servidor) ){
            logmacro("\n%s\n","No fue posible conectar el receptor al servidor");
            return EXIT_FAILURE;
        }

        /* Creo un correo a partir de un archivo */
        crearCorreo(&correoEnviado);

        if ( argc != 1 ){
            if( !i )
                i++;
            logmacro("\n Abriendo archivo: %s\n\n",argv[i]);
            archivoIn=fopen(argv[i],"r");

            /* Chequeo que el archivo se haya abierto correctamente */
            if ( archivoIn == NULL ){
                logmacro("\nError al abrir archivo: %s\n",argv[i]);
                return EXIT_FAILURE;
            }


            if ( importarDesde(&correoEnviado, archivoIn) ){
                logmacro("\nError al importar correo: %s\n",argv[i]);
                return EXIT_FAILURE;
            }
        }

        if ( enviarCorreo(emisor, correoEnviado) ){
            logmacro("\n%s\n","Error al enviar correo.");
            return EXIT_FAILURE;
        }

        logmacro("%s\n", "Enviando correo");

        /* Obtengo la clave y elimino el contenido del canal seguro. */
        obtenerClave(&receptor, canalSeguro);
        eliminarContenido(&canalSeguro);

        /* Creo una variable para contener el correo recibido */
        crearCorreo(&correoRecibido);

        /* Recibo el correo, en caso de error o no encontrarse mensajes
         * continuo con el siguiente.
         */
        if ( recibirProximoCorreo(receptor, &correoRecibido) )
            continue;

        if ( argc != 1 ){
            strcpy(outFilename,argv[i]);
            /* Para cambiar la extension del archivo de .txt a _recv.txt se procede a calcular la
             * pos. de memoria del primer elemento del nombre de archivo, luego a eso se le suma
             * la longitud de todo el nombre (dejandonos posicionados en el /0 del "string") y
             * luego se resta lo que "ocupa" el .txt; una vez realizada esta operacion simplemente
             * se pisa desde esa posicion de memoria con el sufijo que deseamos (_recv.txt).
             */
            memcpy(outFilename+strlen(outFilename)-strlen(DEFAULT_EXTENSION),DEFAULT_SUFFIX,SUFFIX_LENGTH);

            /* Guardo el correo en un archivo */
            logmacro("\n Guardando mensaje en el archivo: %s\n\n",outFilename);

            archivoOut=fopen(outFilename,"w");

            /* Chequeo que el archivo se haya abierto correctamente */
            if ( archivoOut == NULL ){
                logmacro("\n%s\n","Error al abrir/crear el archivo de salida.");
                return EXIT_FAILURE;
            }

            if ( escribirA(correoRecibido, archivoOut) ){
                logmacro("\nError al escribir el correo en el archivo: %s",outFilename);
                return EXIT_FAILURE;
            }

        }else{

            /* Si no recibi argumentos muestro el correo por salida estandar (segun especificaciones)*/
        	fechaHora = getFechaHoraEnviado(correoRecibido);
            getRemitente(correoRecibido, remitente);
            getDestinatarios(correoRecibido, destinatario);
            getAsunto(correoRecibido, asunto);
            getTextoCuerpo(correoRecibido, cuerpo);

            printf("From: %s\n",remitente);
            printf("To: %s\n",destinatario);
            printf("Subject: %s\n",asunto);
            printf("Date: %s\n",ctime(&fechaHora));
            printf("%s\n",cuerpo);
        }

        /* Destruyo las variables que use para los correos */
        destruirCorreo(&correoEnviado);
        destruirCorreo(&correoRecibido);

        /* Desconecto los clientes de correo del servidor */
        desconectar(&emisor);
        desconectar(&receptor);

        /* Destruyo los clientes, el canal seguro y el servidor */
        destruirCanalSeguro(&canalSeguro);
        destruirClienteCorreo(&emisor);
        destruirClienteCorreo(&receptor);
        destruirServidor(&servidor);

    }

	logmacro("%s\n", "Simulacion finalizada");

	return EXIT_SUCCESS;
}
